﻿using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using EpiNEXT.Core.Collections;
using EpiNEXT.Core.Fields;

namespace EpiNEXT.Core.Validation
{
    [Serializable()]
    public sealed class MultiCondition : ConditionBase
    {
        public MultiCondition()
            : base()
        {

        }

        /// <summary>
        /// A list of expressions that comprise this condition
        /// </summary>
        [XmlArray("Conditions")]
        [XmlArrayItem("SingleCondition", typeof(SingleCondition))]
        [XmlArrayItem("MultiCondition", typeof(MultiCondition))]
        public ConditionCollection Conditions { get; set; }

        public override List<IField> GetFieldsInCondition(DataEntryInstrument form)
        {
            List<IField> fields = new List<IField>();

            foreach (ICondition condition in this.Conditions)
            {
                fields.AddRange(condition.GetFieldsInCondition(form));
            }

            return fields;
        }

        public override bool Evaluate(Record record)
        {
            DataEntryInstrument form = record.Form;

            if (Conditions == null)
            {
                return true;
            }
            else if (Conditions.Count == 1)
            {
                // One expression; evaluate it
                ICondition condition = Conditions[0];
                return condition.Evaluate(record);
            }
            else if (Conditions.Count == 2)
            {
                // Two expressions; evaluate both using the supplied operator
                ICondition condition1 = Conditions[0];
                ICondition condition2 = Conditions[1];

                bool valid1 = condition1.Evaluate(record);
                bool valid2 = condition2.Evaluate(record);

                switch (Conditions.LogicalOperator)
                {
                    case LogicalOperators.AND:
                        return (valid1 && valid2);
                    case LogicalOperators.OR:
                        return (valid1 || valid2);
                    case LogicalOperators.XOR:
                        return (valid1 ^ valid2);
                }
            }
            else if (Conditions.Count > 2)
            {
                bool isValid = false;

                int i = 0;
                // More than two expressions; evaluate all using the supplied operator
                foreach (ICondition condition in Conditions)
                {
                    bool currentConditionValidation = condition.Evaluate(record);

                    if (i == 0)
                    {
                        isValid = currentConditionValidation;
                    }

                    if (i > 0)
                    {
                        switch (Conditions.LogicalOperator)
                        {
                            case LogicalOperators.AND:
                                isValid = (isValid && currentConditionValidation);
                                break;
                            case LogicalOperators.OR:
                                isValid = (isValid || currentConditionValidation);
                                break;
                            case LogicalOperators.XOR:
                                throw new NotImplementedException("XOR not supported for more than two conditions");
                        }
                    }

                    i++;
                }

                return isValid;
            }

            return true;
        }
    }
}
